/*
 * ksContainerBlockReader.cpp
 *
 *  Created on: Aug 3, 2011
 *      Author: wang
 */

#include "ksContainerBlockReader.h"
#include <cstring>
#include <exception>
#include <fstream>
#include "../ksDefines.h"
#include "../ksBlockEntry.h"
#include "../ksAtomBlock.h"
#include "ksAtomBlockReader.h"
#include "ksCatalogAtomBlockReader.h"

using std::exception;

void ksContainerBlockReader::loadHeader(unsigned char *data, unsigned int length)
{
    int copySize = sizeof(_containerBlockHeader);
    memcpy(&_blockHeader.container, data, copySize);
    description();
}

void ksContainerBlockReader::loadEntries(unsigned char *data, unsigned int length)
{
    _entries.clear();

    unsigned short entryCount = _blockHeader.container.entryCount;

    unsigned char* entryBegin = data;
    unsigned int entryLength = length;
    int skip = 0;
    for (int index = 0; index < entryCount; index++)
    {
        ksBlockEntry* entry = new ksBlockEntry;
        skip = entry->load(entryBegin, entryLength);
        if (skip <= 0)
        {
            delete entry;
            std::cout << "error occurred." << std::endl;
            throw exception();
            break;
        }
        _entries.push_back(entry);
        entryBegin += skip;
        entryLength -= skip;
    }
}

void ksContainerBlockReader::load(std::ifstream & input)
{
    if (input.good())
    {
        int blockOffset = input.tellg();

        // load header
        unsigned char headerBuffer[kContainerBlockHeaderLength] = { 0 };
        input.read((char*) headerBuffer, kContainerBlockHeaderLength);
        loadHeader(headerBuffer, kContainerBlockHeaderLength);
        std::cout << "file position:" << input.tellg() << std::endl;

        // load entry
        int entryCount = _blockHeader.container.entryCount;
        int entryLength = entryCount * kBlockEntryLength;
        unsigned char* entryBuffer = new unsigned char[entryLength];
        input.read((char*) entryBuffer, entryLength);
        loadEntries(entryBuffer, entryLength);
        delete[] entryBuffer;

        std::vector<ksBlockEntry*>::iterator it;
        for (it = _entries.begin(); it != _entries.end(); it++)
        {
            ksBlockEntry* entry = *it;
            unsigned short entryType = entry->type();
            if (kBlockMainTag == entryType || kBlockPartTag == entryType || kBlockFileTag == entryType)
            {
                ksContainerBlockReader* block = new ksContainerBlockReader();

                block->type(entry->type());
                block->id(entry->id());

                input.seekg(blockOffset + _blockHeader.container.entryTableSize + entry->offset(), std::ifstream::beg);
                block->load(input);
                _blocks.push_back(block);
            }
            else
            {
                ksAtomBlockReader* block = NULL;
                if (entryType == kBlockCatalog)
                {
                    block = new ksCatalogAtomBlockReader();
                }
                else
                {
                    block = new ksAtomBlockReader();
                }

                block->type(entry->type());
                block->id(entry->id());

                int newBlockOffset = blockOffset + _blockHeader.container.entryTableSize + entry->offset();
                input.seekg(newBlockOffset, std::ifstream::beg);
                block->load(input);
                _blocks.push_back(block);
            }
        }
    }
}

void ksContainerBlockReader::extract(const char *path)
{
    std::vector<ksBlock*>::iterator it;
    for (it = _blocks.begin(); it != _blocks.end(); it++)
    {
        (*it)->reader()->extract(path);
    }
}
